package mspbots.timedoctor.client.tigger;

import com.fasterxml.jackson.databind.JsonNode;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import mspbots.timedoctor.client.core.BaseGenericService;
import mspbots.timedoctor.client.core.api.v2.*;
import mspbots.timedoctor.client.oauth.AuthorizationCode;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * ScheduledAsyncService
 *
 * @author Alex bob(https://github.com/vnobo)
 * @date Created by 2019/6/19
 */
@Log4j2
@Service
@RequiredArgsConstructor
public class AsyncScheduleEvent extends BaseGenericService {


    private final CompaniesV2Service companiesV2Service;
    private final ProjectsV2Service projectsV2Service;
    private final FoldersV2Service foldersV2Service;
    private final UsersV2Service usersV2Service;
    private final TasksV2Service tasksV2Service;

    // 30 minutes
    @Scheduled(fixedDelay = 1000 * 60 * 30)
    public void syncTimeDoctorV2Data() {
        final String syncId = UUID.randomUUID().toString();
        final long start = System.nanoTime();
        this.reactiveRedisTemplate.keys(this.REDIS_KEY_PREFIX + "oauth:*:authorization")
                .subscribe(key -> {
                    String[] kesArray = StringUtils.delimitedListToStringArray(key, ":");
                    Long tenantId = Long.parseLong(kesArray[kesArray.length - 2]);
                    this.reactiveRedisTemplate.opsForValue()
                            .get(this.REDIS_KEY_PREFIX + this.ONOFF + tenantId)
                            .subscribe(val -> {
                                log.info("Get tenant {} sync 'ON|OFF' status: {}", tenantId, val);
                                if ("ON".equalsIgnoreCase(String.valueOf(val))) {
                                    this.reactiveRedisTemplate.opsForValue()
                                            .get(key)
                                            .map(value -> this.objectMapper.convertValue(value, AuthorizationCode.class))
                                            .subscribe(authorizationCode -> {
                                                        log.info("Tenant {} sync id: {}", tenantId, syncId);
                                                        String company = authorizationCode.getExtend().get("companies").get(0).get("id").asText();
                                                        this.companiesV2Service.async(syncId, tenantId, company);
                                                        this.projectsV2Service.async(syncId, tenantId, company);
                                                        this.foldersV2Service.async(syncId, tenantId, company);
                                                        this.tasksV2Service.async(syncId, tenantId, company);
                                                    }
                                                    , err -> {
                                                        long milliseconds = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
                                                        log.error("Auto sync error: {}", err.getMessage());
                                                        this.sendLog(syncId, tenantId, milliseconds, "auto_synchronise", FAIL_CODE, err.getMessage(), null);
                                                    });
                                }
                            });
                });

    }

    // 3 minutes
    @Scheduled(fixedDelay = 1000 * 60 * 3)
    public void syncTimeDoctorV2WorkLogs() {
        final String syncId = UUID.randomUUID().toString();
        final long start = System.nanoTime();
        this.reactiveRedisTemplate.keys(this.REDIS_KEY_PREFIX + "oauth:*:authorization")
                .subscribe(key -> {
                    String[] kesArray = StringUtils.delimitedListToStringArray(key, ":");
                    Long tenantId = Long.parseLong(kesArray[kesArray.length - 2]);
                    this.reactiveRedisTemplate.opsForValue()
                            .get(this.REDIS_KEY_PREFIX + this.ONOFF + tenantId)
                            .subscribe(val -> {
                                log.info("Get tenant {} sync 'ON|OFF' status: {}", tenantId, val);
                                if ("ON".equalsIgnoreCase(String.valueOf(val))) {
                                    this.reactiveRedisTemplate.opsForValue()
                                            .get(key)
                                            .map(value -> this.objectMapper.convertValue(value, AuthorizationCode.class))
                                            .subscribe(authorizationCode -> {
                                                        log.info("Tenant {} sync id: {}", tenantId, syncId);
                                                        String company = authorizationCode.getExtend().get("companies").get(0).get("id").asText();
                                                        this.usersV2Service.async(syncId, tenantId, company);
                                                    }
                                                    , err -> {
                                                        long milliseconds = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
                                                        log.error("Auto sync error: {}", err.getMessage());
                                                        this.sendLog(syncId, tenantId, milliseconds, "auto_synchronise", FAIL_CODE, err.getMessage(), null);
                                                    });
                                }
                            });
                });

    }

    public void manualSynchronise(Map<String, Object> params) {
        JsonNode jsonParams = objectMapper.convertValue(params, JsonNode.class);
        final String syncId = UUID.randomUUID().toString();
        final long start = System.nanoTime();
        Long tenantId = Long.parseLong(params.get("tenantId").toString());

        this.reactiveRedisTemplate.opsForValue()
                .set(this.REDIS_KEY_PREFIX + this.ONOFF + tenantId, "ON")
                .subscribe(result -> {
                    log.info("Tenant {} manual sync 'ON' result: {}", tenantId, result);
                    this.reactiveRedisTemplate.keys(this.REDIS_KEY_PREFIX + "oauth:" + tenantId + ":authorization")
                            .switchIfEmpty(empty -> {
                                long milliseconds = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
                                log.error("Manual sync fail, no authorization.");
                                this.sendLog(syncId, tenantId, milliseconds, "manual_synchronise", FAIL_CODE, "Manual sync fail, no authorization.", jsonParams);
                            })
                            .subscribe(key ->
                                            this.reactiveRedisTemplate.opsForValue().get(key)
                                                    .map(value -> this.objectMapper.convertValue(value, AuthorizationCode.class))
                                                    .subscribe(authorizationCode -> {
                                                        String company = authorizationCode.getExtend().get("companies").get(0).get("id").asText();
                                                        this.companiesV2Service.async(syncId, tenantId, company);
                                                        this.projectsV2Service.async(syncId, tenantId, company);
                                                        this.foldersV2Service.async(syncId, tenantId, company);
                                                        this.usersV2Service.async(syncId, tenantId, company);
                                                        this.tasksV2Service.async(syncId, tenantId, company);
                                                    })
                                    , err -> {
                                        long milliseconds = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
                                        log.error("Tenant {} manual sync error: {}", tenantId, err.getMessage());
                                        this.sendLog(syncId, tenantId, milliseconds, "manual_synchronise", FAIL_CODE, err.getMessage(), jsonParams);
                                    });
                });

    }


    public void manualOnoffSynchronise(Map<String, Object> params) {
        JsonNode jsonParams = objectMapper.convertValue(params, JsonNode.class);
        final String syncId = UUID.randomUUID().toString();
        Long tenantId = Long.parseLong(params.get("tenantId").toString());
        String onoff = params.get("onoff").toString();
        final long start = System.nanoTime();
        this.reactiveRedisTemplate.opsForValue()
                .set(this.REDIS_KEY_PREFIX + this.ONOFF + tenantId, onoff)
                .subscribe(result -> {
                            long milliseconds = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
                            log.debug("Tenant {} manual sync '{}' result: {}", tenantId, onoff, result);
                            this.sendLog(syncId, tenantId, milliseconds, "manual_onoff_synchronise", SUCCESS_CODE, null, jsonParams);
                        }
                        , err -> {
                            long milliseconds = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
                            log.error("Tenant {} manual sync '{}' error: {}", tenantId, onoff, err.getMessage());
                            this.sendLog(syncId, tenantId, milliseconds, "manual_onoff_synchronise", FAIL_CODE, err.getMessage(), jsonParams);
                        });

    }
}
